home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-10-04 | 49.9 KB | 1,710 lines |
- package sub_arctic.lib;
-
- import sub_arctic.input.*;
- import sub_arctic.output.*;
- import sub_arctic.lib.sub_arctic_error;
- import sub_arctic.constraints.std_function;
- import sub_arctic.constraints.std_constraint_consts;
-
- import java.awt.Font;
- import java.awt.FontMetrics;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import java.awt.Color;
-
- /**
- * This is a class for keeping around enough state process an
- * HTML document. It is probably not of interest to anyone
- * other than programmers implementing support for new HTML tags.
- * If you want to <I>use</I> HTML in your sub_arctic interfaces,
- * look at the text_flow class. <P>
- *
- * This class keeps track of various display characteristics
- * and the current contextual information. It also uses methods to
- * generate the various interactors that go on the display and these
- * may be overridden to provide support for new (and more complex)
- * HTML tags. <P>
- *
- * Basically this class keeps a static variable which is the
- * current "state" of the HTML parse. It is ostensibly a stack
- * but because it is searched it various orders, it is implemented
- * with a vector. You can use "push_element" and pop_element to
- * pop elements of this class onto and off this stack. Thus,
- * when a <B> tag is seen a new html_element is pushed
- * onto the stack to note the font change; when the matching
- * </B> tag is reached, the element is popped off the stack. <P>
- *
- * The static methods calculate_font(), calculate_color(), and
- * calculate_indent() are used to compute the current font, color
- * and indentation level whenever the stack is manipulated.
- * Thus the variable _current_font is always the up-to-date
- * value to use for creating new screen text. If you put values
- * in the public slots of the html_element for your specific
- * subclass, these methods will pick those values up and add them
- * to the current "context" for drawing operations. <P>
- *
- * The static method calculate_functions performs an <B>important</B>
- * speed optimization that users of this code must understand.
- * Since you want your new subclasses of html_element to work
- * "in context" with other instances of other subclasses,
- * you must "advertise" which functions of the API you implement.
- * Once you have done so, calculate_functions will determine
- * when to call your object and when to ignore it. This is
- * a speed optimization which allows us to avoid walking the
- * state stack every time we want to insert a word of text (or
- * do any other parsing operation). For example, the subclass
- * numbered_element advertises that it implements the method
- * list_item_prefix because all it cares about doing is inserting
- * the correct prefix when a new LI tag is seen. A more complex
- * example might be if you wanted some "context dependent" tag
- * handling (such as supporting new tags which are only valid
- * inside other tags) you would advertise that you implement
- * the handle_tag operation. <P>
- *
- * The way you advertise what functions you implement is by
- * overriding the method functions_implemented and returning a set
- * of constants (bitwise ored together) from the static constants
- * below. <P>
- *
- * The biggest weakness of this object right now is that it really
- * can't handle anything that spans rows. Once we finish putting
- * up a row, we throw everything about it away. This is probably
- * going to make it hard in the future to do "align=RIGHT" type
- * of stuff in IMG tags since to make it look nice it should span
- * multiple rows of text.<P>
- *
- */
- public class html_element implements std_constraint_consts {
- /*****************************************************************/
- /* STATIC CONSTANTS */
- /*****************************************************************/
- /*
- * These are the static constants that you should return
- * from the method functions_implemented
- * (ored together) for the set of functions your object
- * implements
- */
- public static final int LIST_ITEM_PREFIX = 1;
- public static final int HANDLE_TAG = 2;
- public static final int ADD_SPACE = 4;
- public static final int ADD_WORD = 8;
- public static final int END_OF_LINE = 16;
- public static final int ADD_LIST_ITEM = 32;
- public static final int HANDLE_AMP = 64;
- public static final int STRING_END = 128;
- public static final int PARAGRAPH_END = 256;
- public static final int PARAGRAPH_START = 512;
-
- /*****************************************************************/
- /* STATIC STATE VARIABLES */
- /*****************************************************************/
- /**
- * This is basically a data structure for keeping track of
- * the current state of the HTML parse so we can image correctly.
- * We add html_elements to it and it gets walked to do things
- * like generate the current font and the current indentation level.
- */
- protected static Vector state;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is where we store the current font as we are doing a
- * layout pass.
- */
- protected static Font _current_font;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This variable is the amount of indentation to use if a new line is
- * called for.
- */
- protected static int _current_indent;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the row that we are currently constructing.
- */
- protected static row _current_row;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the current set of colors in use.
- */
- protected static color_pair _current_colors;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This the text flow we are actually working on.
- */
- protected static text_flow _current_flow;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the current column (which is really the
- * current paragraph) we are building.
- */
- protected static column _current_column;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * LIST_ITEM_PREFIX.
- */
- protected static html_element _current_list_item_prefix;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * HANDLE_TAG
- */
- protected static html_element _current_handle_tag;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * ADD_SPACE
- */
- protected static html_element _current_add_space;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * ADD_WORD
- */
- protected static html_element _current_add_word;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * END_OF_LINE
- */
- protected static html_element _current_end_of_line;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * ADD_LIST_ITEM.
- */
- protected static html_element _current_add_list_item;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * HANDLE_AMP.
- */
- protected static html_element _current_handle_amp;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * STRING_END
- */
- protected static html_element _current_string_end;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * PARAGRAPH_END.
- */
- protected static html_element _current_paragraph_end;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is a pointer to the html_element which is handling
- * PARAGRAPH_START
- */
- protected static html_element _current_paragraph_start;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the string tokenizer we are using.
- */
- protected static StringTokenizer _tokenizer;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the amount of space between children.
- */
- protected static int _inter_child_space = 0;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is where we hold the string we are currently
- * building. This is used for the performance optimization
- * so we don't have so many labels.
- */
- protected static StringBuffer _current_buffer;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is where we keep track of how wide the string
- * is that we have buffered in _current_buffer. This
- * is to avoid having to consult the FontMetrics
- * every time.
- */
- protected static int _current_buffer_width;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the basic font size that the layout algorithm
- * is going to use
- */
- protected static int _basic_font_size=12;
-
- /*****************************************************************/
- /* STATIC METHODS FOR SETTING VARS */
- /*****************************************************************/
-
- /**
- * We need this for people who want to have switches for
- * setting the size. This will not take effect until the
- * next parse of an HTML document.
- *
- * @param int s the new basic font size
- */
- public static void set_basic_font_size(int s) {
- _basic_font_size=s;
- }
-
- /*****************************************************************/
- /* STATIC METHODS FOR PARSING HTML */
- /*****************************************************************/
-
- /**
- * This function is called to initialize the parse and get the
- * state stack set up for future things.
- *
- * @param text_flow tf the text_flow we are laying out
- * @param String text the text to put in the text_flow
- */
- protected static void init(text_flow tf,String text) {
-
- /* this guy is the root! */
- html_element he=new html_element(true);
-
- /* set the state up */
- state=new Vector(50);
-
- /* set up the tokenizer */
- _tokenizer=new StringTokenizer(text," \t\n<",true);
-
- /* set the current text flow */
- _current_flow=tf;
-
- /* get the string buffer read */
- _current_buffer=new StringBuffer();
-
- /* set the current column to be a column with the same width as
- * the current flow */
- /* DANGER: IF YOU MODIFY THIS don't forget to modify
- the one in paragraph_start() too */
- _current_column=new column(0 /*ignored*/,0 /* ignored */,
- _current_flow.w()-(2*_current_flow.border()),
- 10 /* ignored */,
- 0 /* no border because parent is doing it*/,
- _current_flow.interchild_space(),
- false,false,false /* crucial */,
- column.LEFT_JUSTIFIED,
- null);
-
- /* we want these guys to size their height by their children */
- _current_column.set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0));
-
- /* this is something of a hack, but we have a dependency
- * that is tough to get around. You'd like it to be the
- * case that every time you change the state stack the
- * current string buffered up is flushed into the
- * display. However, we are trying to get the state stack
- * initialized right now and thus _current_string_end
- * won't have a value yet. So, we are going to initialize
- * it to a dead value, knowing it will get overwritten
- * the first time the functions get calculated. trust me.*/
- _current_string_end=new html_element();
-
- /* default font at font size in the text flow's font */
- he.font_name=_current_flow.font_name();
-
- /* set the font size to be the one for this flow */
- set_basic_font_size(_current_flow.font_size());
- he.font_size=_basic_font_size;
-
- /* push it on the stack */
- push_element(he);
-
- /* setup the first row */
- /* WARNING: if you modify this don't forget to modify the one
- * in end_of_line */
- _current_row=new row(0/*border */,_inter_child_space,
- false,false,row.BOTTOM_JUSTIFIED);
-
- /* this setting of the size to zero is to insure that the row's
- initial width (if any) could throw off the calculations of
- the size of a row*/
- _current_row.set_w(0);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is called to handle the end of the HTML parse
- * and general close down operations.
- *
- */
- protected static void finish() {
-
- /* clean up */
- _current_end_of_line.end_of_line(0,false);
-
- /* finish this paragraph */
- _current_paragraph_end.paragraph_end();
-
- /* we're done, so prevent things from working if we got into
- this code again */
- _current_row=null;
- _current_column=null;
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function pushes things on the state vector. It also
- * forces a recalculation of the current indentation level
- * and current font.
- *
- * @param html_element he the html element to push onto the state
- */
- protected static void push_element(html_element he) {
-
- /* before we go changing the state, we need to tell the
- string handling code what's happening */
- _current_string_end.string_end();
- state.addElement(he);
-
- /* recalculate the state */
- calculate_font();
- calculate_indent();
- calculate_colors();
- calculate_functions();
- }
-
- //had:
- //* @exception bad_value PROPAGATED
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function removes things from the state vector. It
- * also forces a recalculation of the current indentation
- * level.
- *
- * @return html_element although the element removed is returned, its not
- * clear what use that is.
- */
- protected static html_element pop_element() {
- html_element el;
-
- if (state.isEmpty()) {
- throw new sub_arctic_error("State stack empty! Probably caused by " +
- "badly formed HTML");
- }
-
- /* before we go changing the state, we need to tell the
- string handling code what's happening */
- _current_string_end.string_end();
- el=(html_element)state.elementAt(state.size()-1);
- state.removeElementAt(state.size()-1);
-
- /* recalculate the state */
- calculate_font();
- calculate_indent();
- calculate_colors();
- calculate_functions();
- /* return the element */
- return el;
- }
-
- //had:
- //* @exception bad_value is thrown if the stack is empty when you try to pop it
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function calculates the current font by walking the
- * state vector. It's policy is that it uses the most current
- * font and the most current font size. It does a binary or of
- * all font modifiers found. Note that the the state is walked
- * from oldest element to newest element.
- *
- */
- protected static void calculate_font() {
- String name=null;
- int size=0,mods=0,i;
- html_element he;
-
- /* validity check */
- if (state.size()==0) {
- throw new sub_arctic_error("States stack is empty!");
- }
-
- /* walk the state */
- for (i=0; i<state.size(); ++i) {
- he=(html_element)state.elementAt(i);
-
- /* copy the values */
- if (he.font_name!=null) {
- name=he.font_name;
- }
- if (he.font_size!=0) {
- size=he.font_size;
- }
-
- /* always or in this value... it can't hurt! */
- mods|=he.font_modifier;
- }
-
- /* validity check the result */
- if ((name==null) ||
- (size==0)) {
- throw new sub_arctic_error("The state stack didn't produce a valid font");
- }
-
- /* set the font */
- _current_font=new Font(name,mods,size);
- }
-
- //had:
- //* @exception bad_value will be thrown if insufficient font information
- //* is found to generate a font.
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function computes the current indent level by walking
- * down the state and adding up the current amount of indentation.
- */
- protected static void calculate_indent() {
- int i,indent=0;
- html_element he;
-
- /* validity check */
- if (state.size()==0) {
- throw new sub_arctic_error("States stack is empty!");
- }
-
- /* walk the state */
- for (i=0; i<state.size(); ++i) {
- he=(html_element)state.elementAt(i);
- /* ok add this one in */
- indent+=he.indent_contribution;
- }
-
- /* totaled it up, stuff it in */
- _current_indent=indent;
- }
-
- //had:
- //* @exception bad_value is thrown if the state stack is empty
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function calculates the current color to be drawing
- * in. If it doesn't find anything, it uses the default system
- * colors.
- */
- public static void calculate_colors() {
- int i;
- html_element he;
- color_pair cp=null;
-
- /* walk the state */
- for (i=state.size()-1; i>=0; --i) {
- he=(html_element)state.elementAt(i);
- if (he.colors!=null) {
- cp=he.colors;
- break;
- }
- }
-
- /* figure it out ... if its null then we use the system defaults */
- if (cp==null) {
- _current_colors=manager.default_color_pair();
- } else {
- _current_colors=cp;
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function calculates the current set of objects which are
- * handling parsing operations. This basically walks backwards
- * through the state stack looking at what functions each
- * object is advertising that it implements. If one is found,
- * it is put in the slot for whatever functions it implements.
- * This process proceeds until the root is encountered or
- * state stack is emptied.
- *
- */
- public static void calculate_functions() {
- int i;
- int implemented;
- html_element el;
-
- /* we start out looking for everything */
- int mask=LIST_ITEM_PREFIX | HANDLE_TAG | ADD_SPACE | END_OF_LINE |
- ADD_LIST_ITEM | ADD_WORD | HANDLE_AMP | STRING_END | PARAGRAPH_END |
- PARAGRAPH_START;
-
- /* work our way backwards */
- for (i=state.size()-1; i>=0; --i) {
- el=(html_element)state.elementAt(i);
-
- /* retrieve the ones they are using */
- implemented=el.functions_implemented();
-
- /* test for function */
- if (((mask & LIST_ITEM_PREFIX)!=0) &&
- ((implemented & LIST_ITEM_PREFIX)!=0)) {
- _current_list_item_prefix=el;
- /* we don't need it anymore */
- mask ^= LIST_ITEM_PREFIX;
- }
-
- /* test for function */
- if (((mask & HANDLE_TAG)!=0) &&
- ((implemented & HANDLE_TAG)!=0)) {
- _current_handle_tag=el;
- /* we don't need it anymore */
- mask ^= HANDLE_TAG;
- }
-
- /* test for function */
- if (((mask & ADD_SPACE)!=0) &&
- ((implemented & ADD_SPACE)!=0)) {
- _current_add_space=el;
- /* we don't need it anymore */
- mask ^= ADD_SPACE;
- }
-
- /* test for function */
- if (((mask & ADD_WORD)!=0) &&
- ((implemented & ADD_WORD)!=0)) {
- _current_add_word=el;
- /* we don't need it anymore */
- mask ^= ADD_WORD;
- }
-
- /* test for function */
- if (((mask & END_OF_LINE)!=0) &&
- ((implemented & END_OF_LINE)!=0)) {
- _current_end_of_line=el;
- /* we don't need it anymore */
- mask ^= END_OF_LINE;
- }
-
- /* test for function */
- if (((mask & ADD_LIST_ITEM)!=0) &&
- ((implemented & ADD_LIST_ITEM)!=0)) {
- _current_add_list_item=el;
- /* we don't need it anymore */
- mask ^= ADD_LIST_ITEM;
- }
-
- /* test for function */
- if (((mask & HANDLE_AMP)!=0) &&
- ((implemented & HANDLE_AMP)!=0)) {
- _current_handle_amp=el;
- /* we don't need it anymore */
- mask ^= HANDLE_AMP;
- }
-
- /* test for function */
- if (((mask & STRING_END)!=0) &&
- ((implemented & STRING_END)!=0)) {
- _current_string_end=el;
- /* we don't need it anymore */
- mask ^= STRING_END;
- }
-
- /* test for function */
- if (((mask & PARAGRAPH_END)!=0) &&
- ((implemented & PARAGRAPH_END)!=0)) {
- _current_paragraph_end=el;
- /* we don't need it anymore */
- mask ^= PARAGRAPH_END;
- }
-
- /* test for function */
- if (((mask & PARAGRAPH_START)!=0) &&
- ((implemented & PARAGRAPH_START)!=0)) {
- _current_paragraph_start=el;
- /* we don't need it anymore */
- mask ^= PARAGRAPH_START;
- }
-
- /* if we have all the functions, we can bail out */
- if (mask==0) break;
- }
-
- /* we are ostensibly done, we should check to make sure everything
- * worked */
- if (mask!=0) {
- throw new sub_arctic_error("Not all parsing functions implemented");
- }
- }
-
- //had:
- //* @exception bad_value gets thrown if it can't find any object on the
- //* state stack willing to handle one of the functions.
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is useful for "backchaining" in the state
- * stack to an "older" version of a function. E.g. you are
- * implementing some new context-dependent tags and you find
- * a tag you are not interested in, just call this function
- * with the HANDLE_TAG mask and yourself as the object to
- * look behind and you'll get returned an object on which
- * you can call the handle_tag method and hand it the tag
- * it should process.<p>
- *
- * This function might also be useful for finding out who
- * is "behind" you in the context. E.g. if you wanted to
- * make the numbering tags do dots between subordinate
- * lists. E.g. the second level list would 2.1, 2.2,
- * 2.3, etc.
- *
- * @param int fn the function code to find (must be one of the
- * static constants above.
- * @param html_element elem the object to start looking "behind" (note that
- * this function will return null if the element is
- * the root).
- * @return html_element element implementing the function in question.
- */
-
- public static html_element previous_function(int fn, html_element elem) {
- int index=state.indexOf(elem),i;
- html_element el;
-
- /* bad element? */
- if (index==-1) {
- throw new sub_arctic_error("Element is not in the state stack");
- }
-
- /* the root? */
- if (index==0) {
- return null;
- }
-
- /* normal case */
- for (i=index-1; i>=0; --i) {
- el=(html_element)state.elementAt(i);
- if ((el.functions_implemented() & fn)!=0) {
- return el;
- }
- }
-
- /* didn't find it, something's wrong */
- throw new sub_arctic_error("Unable to locate the function " + fn +
- " in any object in the state stack");
- }
-
- //had:
- //*@exception bad_value is thrown if no matching object is found (this
- //* shouldn't happen if the root is set up right) or you
- //* pass a bad function constant or if the object passed
- //* as the element is not in the state stack.
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the main driver function for this parsing operation.
- */
- public static void parse() {
- String tok;
-
- /* loop over all the tokens */
- while (_tokenizer.hasMoreTokens()) {
-
- /* look for a normal token */
- tok=_tokenizer.nextToken(" \t\n<&");
-
- /* is it the beginning of a tag ? */
- if (tok.indexOf("<")!=-1) {
-
- /* fish out the tag by changing the delimiter */
- tok=_tokenizer.nextToken(">");
-
- /* tell the tag handling process about it */
- _current_handle_tag.handle_tag(tok);
-
- /* we know there is a greater than pending so lets get
- rid of it */
- _tokenizer.nextToken(">");
- continue;
- }
-
- /* if it starts with an ampersand its a special */
- if (tok.startsWith("&")) {
- tok=_tokenizer.nextToken(";");
- _current_handle_amp.handle_amp(tok);
-
- /* get rid of the extra ; */
- _tokenizer.nextToken(";");
- continue;
- }
-
- /* it doesn't have a less than in it, so let's try looking
- for space, newline and or tab */
- if ((tok.indexOf(" ")!=-1) ||
- (tok.indexOf("\t")!=-1) ||
- (tok.indexOf("\n")!=-1)) {
- /* its some sort of whitespace, tell us about it */
- _current_add_space.add_space();
- continue;
- }
-
- /* ok, we now know its a plain old word so let's just add it */
- _current_add_word.add_word(tok);
- }
-
- /* we are done with it */
- finish();
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function determines if a row is empty or not.
- * @param row r the row to check
- * @return boolean true if the row is empty
- */
- public static boolean row_is_empty(row r) {
- if ((r.num_children()==0) ||
- ((r.num_children()==1) &&
- (r.child(0) instanceof spacer))) {
- return true;
- }
- return false;
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /*****************************************************************/
- /* INSTANCE VARIABLES */
- /*****************************************************************/
- /**
- * This variable tells us if we are the root of the state stack
- * or not. If this is true we will export all the functions in
- * the API to their default values.
- */
- private boolean root;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** use this for tags that actually change the base font */
- /* if this is NULL it is ignored */
- public String font_name;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** use this for tags that SET the size of the font */
- /* if this is 0 it gets ignored */
- public int font_size;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** this is a font modifier for things like bold and italic
- * it gets ORed into the font modifier mask ... since plain is
- * zero you can just let plain be the value if you don't want
- * anything snazzy */
- public int font_modifier;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /** this is the amount of indentation that is tag's contribution
- * to future rows. Its not an absolute amount but a contribution */
- public int indent_contribution;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is the color_pair to use for drawing the text. If you
- * don't do anything, you'll get null and the system will use
- * the default system colors.
- */
- public color_pair colors;
-
- /*****************************************************************/
- /* OVERRIDABLE METHODS */
- /*****************************************************************/
-
- /**
- * Construct a html_element. This constructor makes all the fields
- * have ignored values and this object is "dead" with respect
- * to what functions it implements.
- */
- public html_element() {
- root=false;
- font_name=null;
- font_size=0;
- font_modifier=0;
- indent_contribution=0;
- colors=null;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Construct an HTML element which is the root of the state
- * stack by passing true here. You should ONLY do this if you
- * intend that all objects below this one in the state stack
- * have their functions ignored (which is only likely to be
- * useful if you are putting this at the bottom of the
- * state stack. This still makes all the fields have
- * their default (ignored) values.
- *
- * @param boolean b true if this object is a root object.
- *
- */
- public html_element(boolean b) {
- root=b;
- font_name=null;
- font_size=0;
- font_modifier=0;
- indent_contribution=0;
- colors=null;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Override this method to inform the system what parsing
- * methods you handle.
- */
- public int functions_implemented() {
- if (root) {
- /* if we are the root, we implement everything */
- return (LIST_ITEM_PREFIX | HANDLE_TAG | ADD_SPACE | END_OF_LINE |
- ADD_LIST_ITEM | ADD_WORD | HANDLE_AMP | STRING_END |
- PARAGRAPH_END | PARAGRAPH_START);
- } else {
- /* if we aren't a root, we are a pass thru */
- return 0;
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is called to generate a prefix interactor for
- * list elements. It should return an interactor to put right in
- * front of the actual item. Thus subclasses like the
- * numbered_element return a label with the correct number
- * in it for this function...
- *
- */
- public interactor list_item_prefix()
- {
- return null;
- }
-
- //had:
- //* @exception general may be thrown by subclasses
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function gets called to implement a tags behavior. Since
- * Java can't handle pointers to functions, you'll just have to
- * check for each string and then add your code at the appropriate
- * place.
- */
- protected void handle_tag(String tag)
- {
- /*
- * Get the <P> tag
- */
- if (tag.equalsIgnoreCase("P")) {
- spacer sp=new spacer(1,manager.get_metrics(_current_font).getAscent());
- /* finish this line */
- _current_end_of_line.end_of_line(0,false);
- /* finish this paragraph */
- _current_paragraph_end.paragraph_end();
- /* start a new paragraph */
- _current_paragraph_start.paragraph_start();
- /* put a spacer in that is the height of a T */
- _current_row.add_child(sp);
- /* finish that line */
- _current_end_of_line.end_of_line(0,true);
- /* finish blank paragraph */
- _current_paragraph_end.paragraph_end();
- /* start a new paragraph */
- _current_paragraph_start.paragraph_start();
- /* done */
- return;
- }
-
- /*
- * We are going to intentionally ignore the /P tag so things
- * will work ok if people form their HTML "correctly."
- */
- if (tag.equalsIgnoreCase("/P")) {
- return;
- }
-
- /*
- * Get the <BR> tag.
- */
- if (tag.equalsIgnoreCase("BR")) {
- /* line is done */
- _current_end_of_line.end_of_line(0,false);
- return;
- }
-
- /*
- * I don't know why you would use the /BR tag but just in case...
- */
- if (tag.equalsIgnoreCase("/BR")) {
- return;
- }
-
- /**
- * The <CENTER> tag
- */
- if (tag.equalsIgnoreCase("CENTER")) {
-
- /* implies end of line */
- _current_end_of_line.end_of_line(0,false);
-
- /* end of paragraph */
- _current_paragraph_end.paragraph_end();
-
- /* push us the stack */
- push_element(new centered_element());
-
- /* now start a new paragraph */
- _current_paragraph_start.paragraph_start();
- }
-
- /* pop off the center object from the state stack */
- if (tag.equalsIgnoreCase("/CENTER")) {
-
- /* end the line */
- _current_end_of_line.end_of_line(0,false);
-
- /* end of paragraph */
- _current_paragraph_end.paragraph_end();
-
- /* remove us from the state stack */
- pop_element();
-
- /* start over */
- _current_paragraph_start.paragraph_start();
- }
-
- /*
- * The <LI> tag is one that needs to be deal with specially.
- */
- if (tag.equals("LI")) {
- _current_add_list_item.add_list_item();
- return;
- }
-
- /*
- * <B> tag
- */
- if (tag.equalsIgnoreCase("B")) {
- /* create a new element with bold in it and push it */
- html_element he=new html_element();
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /*
- * </B>
- */
- if (tag.equalsIgnoreCase("/B")) {
- pop_element();
- }
-
- /*
- * <I> tag
- */
- if (tag.equalsIgnoreCase("I")) {
- /* create a new element with bold in it and push it */
- html_element he=new html_element();
- he.font_modifier=Font.ITALIC;
- push_element(he);
- }
-
- /*
- * </I>
- */
- if (tag.equalsIgnoreCase("/I")) {
- pop_element();
- }
-
- /**
- * <OL> tag
- */
- if (tag.equalsIgnoreCase("OL")) {
- html_element he = new numbered_element();
- push_element(he);
- }
-
- /*
- * </OL>
- */
- if (tag.equalsIgnoreCase("/OL")) {
- pop_element();
- }
-
- /**
- * <UL> tag
- */
- if (tag.equalsIgnoreCase("UL")) {
- html_element he = new non_numbered_element();
- push_element(he);
- }
-
- /*
- * </UL>
- */
- if (tag.equalsIgnoreCase("/UL")) {
- pop_element();
- }
-
- /*
- * H1 is really big (24 pt)
- */
- if (tag.equalsIgnoreCase("H1")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size+12;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /*
- * H2 is pretty big (16 pt)
- */
- if (tag.equalsIgnoreCase("H2")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size+4;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /*
- * H3 is big (14 pt)
- */
- if (tag.equalsIgnoreCase("H3")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size+2;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /*
- * H4 is normal sized (12 pt)
- */
- if (tag.equalsIgnoreCase("H4")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /**
- * H5 is smaller than normal (10 pt)
- */
- if (tag.equalsIgnoreCase("H5")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size-2;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /**
- * H6 is much smaller than normal (8 pt)
- */
- if (tag.equalsIgnoreCase("H6")) {
- html_element he=new html_element();
- he.font_size=_basic_font_size-4;
- he.font_modifier=Font.BOLD;
- push_element(he);
- }
-
- /**
- * Any tag staring with /H we do the same thing
- */
- if (tag.toUpperCase().startsWith("/H")) {
- pop_element();
- _current_end_of_line.end_of_line(0,false);
- }
-
- /**
- * KBD (keyboard) is one that I use a lot ... You get a courier
- * font for that "computerish" look.
- */
- if (tag.equalsIgnoreCase("KBD")) {
- html_element he=new html_element();
- he.font_name="Courier";
- push_element(he);
- }
-
- /*
- * /KBD
- */
- if (tag.equalsIgnoreCase("/KBD")) {
- pop_element();
- }
-
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function gets called to add whitespace to the display.
- */
- protected void add_space()
- {
- int w;
-
- /* if we are at the left edge of a row, don't bother */
- if (row_is_empty(_current_row) && (_current_buffer.length()==0)) {
- return;
- }
-
- /* ask the manager how wide a space is */
- w=manager.get_metrics(_current_font).stringWidth(" ");
-
- /* are we going to force a new string? */
- if (w+_current_buffer_width >= _current_column.w()) {
- /* don't bother, we'll just end this line */
- _current_end_of_line.end_of_line(0,false);
- return;
- }
-
- /* check to see if there is a string buffer contents now */
- if (_current_buffer.length()!=0) {
- /* the buffer has a string in it, so just add a space char */
- _current_buffer.append(" ");
- /* update how much space the string occupies */
- _current_buffer_width+=w;
- return;
- }
-
- /* nothing buffered, use a spacer */
- spacer sp=new spacer(w,1);
-
- /* put the space in there */
- _current_row.add_child(sp);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function does a calculation to see if the new word will fit
- * on this line. If it does, it just adds it to the current row.
- * If not, it creates a new line and adds the word to that line,
- * without considering if it fits. (Note: This means that words
- * that are wider than the width of the parent just get clipped.)
- *
- * @param String word the text of the word to add
- */
- protected void add_word(String word)
- {
- int parent_width=_current_column.w(), row_width=_current_row.w();
- int string_width;
- FontMetrics fm=manager.get_metrics(_current_font);
-
- /* this is the string we are adding to the display plus
- any buffered text's width */
- string_width=fm.stringWidth(word) + _current_buffer_width;
-
- /* case 1: row is empty or has only a spacer on it: we always
- * put this word on such a line */
- if (row_is_empty(_current_row) && (_current_buffer.length()==0)) {
-
- /* we always add it because otherwise you'll get a blank line
- if this object is larger than the overall flow */
- _current_buffer.append(word);
-
- /* set the buffer width appropriately */
- _current_buffer_width=string_width;
- return;
- }
-
- /* is it too big? */
- if (string_width + row_width + _inter_child_space > parent_width) {
-
- /* case 2: too big ... finish this line and make a new one*/
- _current_end_of_line.end_of_line(0,false);
-
- /* put the string on the next line */
- _current_buffer.append(word);
-
- /* store the amount of text we have left */
- _current_buffer_width=fm.stringWidth(word);
-
- } else {
-
- /* case 3: this is the normal case */
- _current_buffer.append(word);
-
- /* remember how much we have buffered */
- _current_buffer_width=string_width;
- }
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function is called to inform us that the current string
- * needs to be inserted into the row. This allows us (usually)
- * to make one big label for the string, rather than having
- * a string for each word (which really slows things down).
- * If you are implementing tags which will insert things into
- * the current row, you'll probably need to call this function
- * to make sure all the text is there.
- *
- */
- public void string_end()
- {
- label l;
-
- /* empty strings means no work */
- if (_current_buffer.length()==0) return;
-
- /* not empty so make a label */
- l=new label(_current_buffer.toString(),_current_font);
-
- /* fix the spacing */
- l.set_above_spacing(0);
-
- /* XXX should be figuring out the descent size ... it appears that
- the descent size in many cases is WAY larger than it needs to be
- so just use 3 instead XX */
- /* l.set_below_spacing(3); */
- l.set_below_spacing(manager.get_metrics(_current_font).getDescent());
- l.set_h_spacing(0);
-
- /* set the colors */
- l.set_draw_colors(_current_colors);
- _current_row.add_child(l);
-
- /* reset the buffer */
- _current_buffer=new StringBuffer();
-
- /* reset the buffer's size */
- _current_buffer_width=0;
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function gets called to polish off the current line.
- * It takes the current row and adds to the current column
- * and then sets up a new row. If you supply an
- * argument, that much is subtracted from the normal indentation
- * for this row. This is highly useful for keeping things aligned
- * in the presence of list items marking the paragraphs.
- *
- * @param int shift the amount you want subtracted from the indentation.
- * @param boolean hard true if you are sure you want to <I>force</I> the
- * end of a line, even if you are at the beginning of
- * an empty row. Use false to indicate that you just
- * want to start at the beginning of a line.
- */
- public void end_of_line(int shift, boolean hard)
- {
- /* deal with soft end of line */
- if (hard==false) {
- /* is this an empty row? */
- if (row_is_empty(_current_row) && (_current_buffer.length()==0)) {
- /* its empty and they wanted a soft eol */
- return;
- }
- }
-
- /* make sure we put in the buffered text */
- _current_string_end.string_end();
-
- /* put the row we just finished into the column (us) */
- _current_column.add_child(_current_row);
-
- /* WARNING: if you modify this don't forget to modify the one
- * in init(...) */
- _current_row=new row(0,_inter_child_space,
- false,false,row.BOTTOM_JUSTIFIED);
-
- /* this setting of the size to zero is to insure that the row's
- initial width (if any) could throw off the calculations of
- the size of a row*/
- _current_row.set_w(0);
-
- /* if we don't need any space, don't bother */
- if (_current_indent==0) return;
-
- /* we only make this one pixel high because we figure if
- * if any text gets put in it will make the object size right */
- _current_row.add_child(new spacer(_current_indent-shift,1));
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Insert a list item. This ends up causing whatever the current
- * object for this lists iteration to be inserted.
- *
- */
- public void add_list_item()
- {
- interactor marker;
- int i;
- html_element el;
-
- /* get the most current list item implementation */
- marker=_current_list_item_prefix.list_item_prefix();
-
- /* did we get out with nothing found? */
- if (marker==null) {
- System.out.println("BAD <LI> tag! No list in progress!");
- return;
- }
-
- /* the normal thing is to finish the current line and then add the
- marker */
- _current_end_of_line.end_of_line(marker.w(),false);
- _current_row.add_child(marker);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This function gets called to handle putting things it that
- * are used with the ampersand notation.
- * @param String s the string that was between the ampersand and the semicolon
- */
- protected void handle_amp(String tok)
- {
- /* check for ones we know */
- if (tok.equals("quot")) {
- _current_add_word.add_word("\"");
- } else if (tok.equals("lt")) {
- _current_add_word.add_word("<");
- } else if (tok.equals("gt")) {
- _current_add_word.add_word(">");
- }
-
- /* didn't recognize it, give up */
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Handle the end of the current paragraph. This function takes
- * the current column and shoves it into the text flow. It is
- * most commonly called as P is encountered.
- */
- public void paragraph_end() {
- _current_flow.add_child(_current_column);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Handle the start of a new paragraph. This default of
- * this just makes the current column be left justified.
- * It is most commonly called when a P is encountered,
- * but can be called by tags like CENTER.
- */
- public void paragraph_start() {
- /* DANGER: IF YOU MODIFY THIS don't forget to modify
- the one in init() too */
- _current_column=new column(0 /*ignored*/,0 /* ignored */,
- _current_flow.w()-(2*_current_flow.border()),
- 10 /* ignored */,
- 0 /* parent is doing border */,
- _current_flow.interchild_space(),
- false,false,false /* crucial */,
- column.LEFT_JUSTIFIED,
- null);
- _current_column.
- set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0));
- }
- }
- /*****************************************************************/
- /* SOME EXAMPLE CLASSES FOR COMMON TAGS */
- /*****************************************************************/
-
- /**
- * This private class is used to keep track of the numbering
- * and generate the numbers for numbered lists.
- */
- class numbered_element extends html_element {
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * This is what number we are about to output.
- */
- int count;
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Construct a numbered element.
- */
- public numbered_element() {
- super();
- count=1;
- indent_contribution=10;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * We overrode for the purpose of informing the system that
- * we implement the LIST_ITEM_PREFIX method.
- *
- * @return int bitmask of the functions implemented
- */
- public int functions_implemented() {
- return LIST_ITEM_PREFIX;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Generate the next list item prefix, which is a number.
- *
- */
- public interactor list_item_prefix()
- {
- int c=count;
- label l;
-
- /* update count */
- count++;
-
- /* make the label */
- l=new label((new Integer(c)).toString(),_current_font);
- l.set_above_spacing(0);
-
- /* XXX should be figuring out the descent size ... it appears that
- the descent size in many cases is WAY larger than it needs to be
- so just use 3 instead XX */
- /* l.set_below_spacing(3); */
- l.set_below_spacing(manager.get_metrics(_current_font).getDescent());
- l.set_h_spacing(2);
- return l;
- }
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /*---------------------------------------------------------------------*/
-
- /**
- * This is a private class which is used to draw a bullet. This class
- * has no input behavior it just draws a little circle in the middle
- * of its area.
- */
- class bullet extends base_interactor {
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Construct one of these dots.
- * @param int w the width of its bounding rectangle
- * @param int h the height of its bounding rectangle
- */
- public bullet(int w,int h)
- {
- super(0,0,w,h);
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Draw the little dot.
- * @param drawable d the surface to draw on
- */
- public void draw_self_local(drawable d) {
- int x=w()/2,y=h()/2;
- Color c=d.getColor(); // stash this for a sec
-
- d.setColor(html_element._current_colors.foreground());
- d.fillArc(x,y-2,4,4,0,360);
- d.setColor(c);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
-
- /*---------------------------------------------------------------------*/
-
- /**
- * This private class is used to generate the bullets for
- * non-numbered lists.
- */
- class non_numbered_element extends html_element {
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Construct a numbered element.
- */
- public non_numbered_element() {
- super();
- indent_contribution=10;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Generate the next list item prefix, which is a number.
- */
- public interactor list_item_prefix()
- {
- return new bullet(10,manager.get_metrics(_current_font).getAscent()+
- manager.get_metrics(_current_font).getDescent());
- }
-
- //had:
- //* @exception general PROPAGATED
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * We overrode for the purpose of informing the system that
- * we implement the LIST_ITEM_PREFIX method.
- *
- * @return int bitmask of the functions implemented
- */
- public int functions_implemented() {
- return LIST_ITEM_PREFIX;
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
-
- /*---------------------------------------------------------------------*/
-
- /**
- * This class is used to implement the CENTERED tag. It just changes
- * the type of column that gets used to put the rows in.
- */
- class centered_element extends html_element {
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Construct a centered element.
- */
- public centered_element() {
- super();
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * We overrode for the purpose of informing the system that
- * we implement the PARAGRAPH_START method.
- *
- * @return int bitmask of the functions implemented
- */
- public int functions_implemented() {
- return (PARAGRAPH_START);
- }
-
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
-
- /**
- * Handle the start of a new paragraph. We want to create
- * a centered column.
- */
- public void paragraph_start() {
- _current_column=new column(0 /*ignored*/,0 /* ignored */,
- _current_flow.w()-(2*_current_flow.border()),
- 10 /* ignored */,
- 0/* parent is doing border */,
- _current_flow.interchild_space(),
- false,false,false,
- column.CENTER_JUSTIFIED,
- null);
- _current_column.
- set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0));
- }
- /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
- }
-
- /*---------------------------------------------------------------------*/
- /*=========================== COPYRIGHT NOTICE ===========================
-
- This file is part of the subArctic user interface toolkit.
-
- Copyright (c) 1996 Scott Hudson and Ian Smith
- All rights reserved.
-
- The subArctic system is freely available for most uses under the terms
- and conditions described in
- http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html
- and appearing in full in the lib/interactor.java source file.
-
- The current release and additional information about this software can be
- found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
-
- ========================================================================*/
-